
#include "LibSSH2Interface.h"

LibSSH2Interface *LibSSH2Interface::libssh2_iface_singleton = NULL;

/** Get singleton instance (prevents double initialisation) */
LibSSH2Interface &LibSSH2Interface::getInstance()
{
   if (libssh2_iface_singleton == NULL)
      libssh2_iface_singleton = new LibSSH2Interface();
   return *libssh2_iface_singleton;
}

/** Initialises the libssh2 library. */
LibSSH2Interface::LibSSH2Interface()
{
#if LIBSSH2_VERSION_NUM >= 0x010208
   libssh2_init(0);
#else
   #error "libssh2 < 1.2.8 not thread-safe"
#endif
}

/** Finalises the libssh2 library. */
LibSSH2Interface::~LibSSH2Interface()
{
#if LIBSSH2_VERSION_NUM >= 0x010208
   libssh2_exit();
#else
   #error "libssh2 < 1.2.8 not thread-safe"
#endif
}

/** Destroy the singleton. */
void LibSSH2Interface::destroyInstance()
{
   delete libssh2_iface_singleton;
   libssh2_iface_singleton = NULL;
}

/** Parser for the last libSSH2 session error. */
void LibSSH2Interface::func_CheckLastLibSSH2Error(const char *servername, int serverport, int localconnport, LIBSSH2_SESSION *tsession, const char *fpretty, int line)
{
   char *error_msg_buffer = NULL;
   int error_msg_len = 0;
   func_CheckLibSSH2Error(servername, serverport, localconnport, libssh2_session_last_error(tsession, &error_msg_buffer, &error_msg_len, 0), fpretty, line);
   if ((error_msg_len > 0) && (error_msg_buffer != NULL))
      errprintpd("ssh %s:%d(%d) - message - %s", fpretty, line, servername, serverport, localconnport, error_msg_buffer);
}

/** Parser for libSSH2 errors. */
void LibSSH2Interface::func_CheckLibSSH2Error(const char *servername, int serverport, int localconnport, int status, const char *fpretty, int line)
{
   if (status == 0) return;
   const char *terror = "";
   switch (status)
   {
      case LIBSSH2_ERROR_SOCKET_NONE: terror = "no socket (possibly generic error)"; break;
      case LIBSSH2_ERROR_BANNER_RECV: terror = "error receiving banner from server"; break;
      case LIBSSH2_ERROR_BANNER_SEND: terror = "unable to send banner to remote host"; break;
      case LIBSSH2_ERROR_INVALID_MAC: terror = "invalid message authentication code"; break;
      case LIBSSH2_ERROR_KEX_FAILURE: terror = "encryption key exchange with the remote host failed"; break;
      case LIBSSH2_ERROR_ALLOC: terror = "internal allocation failed"; break;
      case LIBSSH2_ERROR_SOCKET_SEND: terror = "unable to send data on socket"; break;
      case LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE: terror = "unrecoverable error while exchanging keys"; break;
      case LIBSSH2_ERROR_TIMEOUT: terror = "operation timed out"; break;
      case LIBSSH2_ERROR_HOSTKEY_INIT: terror = "unable to initialize hostkey import"; break;
      case LIBSSH2_ERROR_HOSTKEY_SIGN: terror = "unable verify hostkey signature"; break;
      case LIBSSH2_ERROR_DECRYPT: terror = "error decrypting packet preamble"; break;
      case LIBSSH2_ERROR_SOCKET_DISCONNECT: terror = "the socket was disconnected"; break;
      case LIBSSH2_ERROR_PROTO: terror = "an invalid SSH protocol response was received on the socket"; break;
      case LIBSSH2_ERROR_PASSWORD_EXPIRED: terror = "password has expired or authentication missing"; break;
      case LIBSSH2_ERROR_FILE: terror = "unable to initialize private key from file"; break;
      case LIBSSH2_ERROR_METHOD_NONE: terror = "no method has been set"; break;
      case LIBSSH2_ERROR_AUTHENTICATION_FAILED: terror = "invalid username/password or public/private key"; break;
      //case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: terror = "the username/public key combination was unrecognised"; break;
      case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: terror = "the username/public key combination was invalid"; break;
      case LIBSSH2_ERROR_CHANNEL_OUTOFORDER: terror = "channel has become out of order"; break;
      case LIBSSH2_ERROR_CHANNEL_FAILURE: terror = "channel has failed"; break;
      case LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED: terror = "channel request denied"; break;
      case LIBSSH2_ERROR_CHANNEL_UNKNOWN: terror = "unknown channel"; break;
      case LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED: terror = "channel window has too much data"; break;
      case LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED: terror = "channel packet has too much data"; break;
      case LIBSSH2_ERROR_CHANNEL_CLOSED: terror = "channel is closed"; break;
      case LIBSSH2_ERROR_CHANNEL_EOF_SENT: terror = "channel EOF was sent"; break;
      case LIBSSH2_ERROR_SCP_PROTOCOL: terror = "an invalid SCP response was received, or an SCP operation errorcode was returned by the server"; break;
      case LIBSSH2_ERROR_ZLIB: terror = "zlib error"; break;
      case LIBSSH2_ERROR_SOCKET_TIMEOUT: terror = "timeout occured on the socket"; break;
      case LIBSSH2_ERROR_SFTP_PROTOCOL: terror = "an invalid SFTP response was received, or an SFTP operation errorcode was returned by the server"; break;
      case LIBSSH2_ERROR_REQUEST_DENIED: terror = "the remote server refused the request"; break;
      case LIBSSH2_ERROR_METHOD_NOT_SUPPORTED: terror = "the requested method is unsupported"; break;
      case LIBSSH2_ERROR_INVAL: terror = "the requested method type was invalid"; break;
      case LIBSSH2_ERROR_INVALID_POLL_TYPE: terror = "invalid descriptor passed to poll"; break;
      case LIBSSH2_ERROR_PUBLICKEY_PROTOCOL: terror = "invalid publickey subsystem response code"; break;
      case LIBSSH2_ERROR_EAGAIN: terror = "operation would block, try again"; break;
      case LIBSSH2_ERROR_BUFFER_TOO_SMALL: terror = "buffer too small"; break;
      case LIBSSH2_ERROR_BAD_USE: terror = "bad use?"; break;
      case LIBSSH2_ERROR_COMPRESS: terror = "compression error"; break;
      case LIBSSH2_ERROR_OUT_OF_BOUNDARY: terror = "data more than maximum limit"; break;
      case LIBSSH2_ERROR_AGENT_PROTOCOL: terror = "failed connecting with agent"; break;
      case LIBSSH2_ERROR_SOCKET_RECV: terror = "unable to receive data on socket"; break;
      case LIBSSH2_ERROR_ENCRYPT: terror = "error encrypting packet preamble"; break;
      case LIBSSH2_ERROR_BAD_SOCKET: terror = "bad socket"; break;
   }
   errprintpd("ssh %s:%d(%d) - %s (error %d)", fpretty, line, servername, serverport, localconnport, terror, status);
}
